/*
 * OpfGenerator.java
 *
 *  created: 20.8.2011
 *  charset: UTF-8
 *  license: MIT (X11) (See LICENSE file for full license)
 */
package cz.mp.k3bg.core;

import cz.mp.k3bg.log.LoggerManager;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;
import static cz.mp.k3bg.Application.EOL;

/**
 * Třída {@code OpfGenerator} vytváří {@code OPF} (Open Packaging Format)
 * soubor ve verzi 2.
 * <p>
 * viz <a href="http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm>
 * Specifikace formátu OPF 2.0.1</a>
 * 
 * @author Martin Pokorný
 * @version 0.1
 * @see BookFiles
 * @see Metadata
 */
public class OpfGenerator extends Generator {

    private static final boolean DEBUG = false;
    private static final Logger logger =
            LoggerManager.getLogger(OpfGenerator.class, DEBUG);
    
    // -----
    
    public OpfGenerator(BookFiles bookFiles, Metadata metadata) {
        super(bookFiles, metadata);
    }
    
    // -----
    
    /**
     * 
     * @throws IOException 
     */
    @Override
    public void generate() throws IOException {
        logger.info("");
        if (bookFiles.getOpf() == null) {
            throw new IllegalStateException("opf=null");
        }
        
        if (bookFiles.getNcx() == null) {
            throw new IllegalStateException("ncx=null");
        }
        
        if (bookFiles.getToc() == null) {
            throw new IllegalStateException("toc=null");
        }
        bookFiles.getToc().initExisting();
        
        logger.config(" opf = " + bookFiles.getOpf().getAbsolutePath());
        
        BufferedWriter writer = bookFiles.getOpf().getBufferedWriter();

        writer.write(createOpfHeader());
        writer.flush();

        writer.write(createOpfMetadata());
        writer.flush();

        writer.write(createOpfManifest());
        writer.flush();

        writer.write(createOpfSpine());
        writer.flush();

        writer.write(createOpfGuide());
        writer.flush();

        writer.write(createOpfFooter());
        writer.flush();
        
        writer.close();
        logger.fine(" opf close");
    }    
    
    /**
     * 
     * @return
     * @throws IOException 
     */
    private String getStartOfBookReference() throws IOException {
        if (bookFiles.getSrcFilesCount() == 0) {
            throw new IllegalStateException("no srcFiles");
        }
        
        HtmlFile srcFile = bookFiles.getSrcHtmlFile(0);
        
//        logger.config(" start of book = " + srcFile.getPath());
        logger.config(" start of book = " 
                + srcFile.getCorrectedPath(bookFiles.getWorkingDirPath()));
        
        return srcFile.getCorrectedPath(bookFiles.getWorkingDirPath());
    }
    
    /**
     * 
     * @return 
     */
    private String createOpfHeader() {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"")
                .append(bookFiles.getOpf().getCharsetName())
                .append("\"?>");
        sb.append(EOL);
        sb.append("<package xmlns=\"http://www.idpf.org/2007/opf\" ")
                .append("version=\"2.0\" unique-identifier=\"BookId\">");
        sb.append(EOL);
        sb.append(EOL);
        return sb.toString();
    }

 
    /**
     * 
     * @return 
     */
    private String createOpfMetadata() {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        sb.append("<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" ")
                .append("xmlns:opf=\"http://www.idpf.org/2007/opf\">");
        sb.append(EOL);
        sb.append("    <dc:title>")
                .append(metadata.getTitle()).append("</dc:title>");
        sb.append(EOL); 

        sb.append("    <dc:creator>")
                .append(metadata.getAuthor()).append("</dc:creator>");
        sb.append(EOL);
        sb.append("    <!-- \"en\", \"cs\", = http://www.i18nguy.com/unicode/language-identifiers.html -->");
        sb.append(EOL);
        sb.append("    <dc:language>")
                .append(metadata.getLang()).append("</dc:language>");  
        sb.append(EOL);
        sb.append("    <dc:identifier id=\"BookId\"");
        if (metadata.isIdIsbn()) {
            sb.append(" opf:scheme=\"ISBN\"");
        }
        sb.append(">").append(metadata.getId()).append("</dc:identifier>");
        sb.append(EOL);
        sb.append("    <dc:publisher>")
                .append(metadata.getPublisher()).append("</dc:publisher>");
        sb.append(EOL);
        sb.append("    <!-- \"Genre\" = ")
                .append("http://www.bisg.org/what-we-do-20-73-bisac-subject-headings-2008-edition.php")
                .append(" (Major Subject) -->");
        sb.append(EOL);
        sb.append("    <dc:subject>")
                .append(metadata.getGenre()).append("</dc:subject>");
        sb.append(EOL);
        sb.append("    <!-- Date of publication = \"yyyy-MM-dd\" -->");
        sb.append(EOL);
        sb.append("    <dc:date>")
                .append(metadata.getPublishDate()).append("</dc:date>");   
        sb.append(EOL);
        sb.append("    <dc:description>")
                .append(metadata.getDescription()).append("</dc:description>");
        sb.append(EOL);        
        sb.append("    <meta name=\"cover\" content=\"")
                .append(bookFiles.getCoverImage().getId()).append("\" />");                
        sb.append(EOL);
        sb.append("</metadata>");
        sb.append(EOL);
        sb.append(EOL);
        
        return sb.toString();
    }

    /**
     * 
     * @return
     * @throws IOException 
     */
    private String createOpfManifest() throws IOException {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        sb.append("<manifest>");
        sb.append(EOL);        
        sb.append("    <!-- HTML content files [mandatory] -->");
        sb.append(EOL);
        sb.append(createOpfHtmlItems());
        sb.append(EOL);
        
        sb.append("    <!-- Style files -->");
        sb.append(EOL);
        sb.append(createOpfStyleItems());
        sb.append(EOL);
                
        sb.append("    <!-- Images -->");
        sb.append(EOL);
        sb.append(createOpfImageItems());
        sb.append(EOL);
        
        sb.append("    <!-- Table of Contents [mandatory] -->");
        sb.append(EOL);
        sb.append(createOpfManifestItem(bookFiles.getNcx()));      
        sb.append(EOL);
        
        sb.append("    <!-- Cover image [mandatory] -->");
        sb.append(EOL);
        sb.append(createOpfManifestItem(bookFiles.getCoverImage()));
        sb.append(EOL);
        
        sb.append("</manifest>");
        sb.append(EOL);
        sb.append(EOL);
        return sb.toString();
    }    

    // pořadí je pevné
    /**
     * 
     * @return
     * @throws IOException 
     */
    private String createOpfHtmlItems() throws IOException {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        
        if (bookFiles.getColophon() != null) {
            bookFiles.getColophon().initExisting();
            sb.append(createOpfManifestItem(bookFiles.getColophon()));
        }        
        if (bookFiles.getPersonalNotes() != null) {
            bookFiles.getPersonalNotes().initExisting();
            sb.append(createOpfManifestItem(bookFiles.getPersonalNotes()));
        }
        if (bookFiles.getLoi() != null) {
            bookFiles.getLoi().initExisting();
            sb.append(createOpfManifestItem(bookFiles.getLoi()));
        }
        
//        if (bookFiles.getToc() != null) {
//            System.out.println(bookFiles.getToc().getPath());
            sb.append(createOpfManifestItem(bookFiles.getToc()));
//        }  

        for (HtmlFile htmlFile : bookFiles.getSrcHtmlFiles()) {
            sb.append(createOpfManifestItem(htmlFile));
        }
        
        return sb.toString();
    }

    
    /**
     * 
     * @return 
     */
    private String createOpfImageItems() {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        Set<ItemImage> images = bookFiles.getImages();
        logger.finer(" images count = " + images.size());
        if (images != null && images.size() > 0) {
            int i=1;
            for (ItemImage image : images) {
                sb.append(createOpfManifestItem(image));
                i++;
            }
        }
        return sb.toString();
    }

    
    /**
     * 
     * @return 
     */
    private String createOpfStyleItems() {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        Set<StyleFile> styleFiles = bookFiles.getStyleFiles();
        if (styleFiles != null && styleFiles.size() > 0) {
            for (ItemFile style : styleFiles) {
                sb.append(createOpfManifestItem(style));
            }
        }
        return sb.toString();
    }

    /**
     * 
     * @param item
     * @return 
     */
    private String createOpfManifestItem(Item item) {
        StringBuilder sb = new StringBuilder();
        sb.append("    <item id=\"")
                .append(item.getId())
                .append("\" media-type=\"")
                .append(item.getMimeType().getValue())
                .append("\" href=\"")
//                .append(item.getPath())
                .append(item.getCorrectedPath(bookFiles.getWorkingDirPath()))
                .append("\" />");
        sb.append(EOL);
        return sb.toString();        
    }


    /**
     * 
     * @return 
     */
    private String createOpfSpine() {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        sb.append("<spine toc=\"")
                    .append(bookFiles.getNcx().getId()).append("\">");
        sb.append(EOL);
        sb.append("    <!-- the spine defines the linear ")
                .append("reading order of the book -->");
        sb.append(EOL);
        
        if (bookFiles.getColophon() != null) {            
            sb.append(createOpfSpineItem(bookFiles.getColophon()));
        }
        if (bookFiles.getPersonalNotes() != null) {            
            sb.append(createOpfSpineItem(bookFiles.getPersonalNotes()));
        }
        if (bookFiles.getLoi() != null) {            
            sb.append(createOpfSpineItem(bookFiles.getLoi()));
        }
        
        sb.append(createOpfSpineItem(bookFiles.getToc()));        
  
        for (HtmlFile htmlFile : bookFiles.getSrcHtmlFiles()) {
            sb.append(createOpfSpineItem(htmlFile));
        }        
        
        sb.append("</spine>");
        sb.append(EOL);
        sb.append(EOL);
        return sb.toString();
    }

    /**
     * 
     * @param item
     * @return 
     */
    private String createOpfSpineItem(Item item) {
        StringBuilder sb = new StringBuilder();
        sb.append("    <itemref idref=\"")
                .append(item.getId())
                .append("\" />");
        sb.append(EOL);
        return sb.toString();
    }
            
    /**
     * 
     * @return
     * @throws IOException 
     */
    private String createOpfGuide() throws IOException {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        sb.append("<guide>");
        sb.append(EOL);
        sb.append("    <!-- type=\"toc\"  [mandatory]: ")
                .append("a link to the HTML table of contents -->");
        sb.append(EOL);
        sb.append("    <reference type=\"toc\" title=\"")
                .append("TOC")
                .append("\" href=\"")
//                .append(bookFiles.getToc().getPath())
                .append(bookFiles.getToc().getCorrectedPath(bookFiles.getWorkingDirPath()))
                .append("\"></reference>");
        sb.append(EOL);
        if (bookFiles.getLoi() != null) {
            // list of illustrations
            sb.append("    <reference type=\"loi\" title=\"")
                    .append("Illustrations")
                    .append("\" href=\"")
//                    .append(bookFiles.getLoi().getPath())
                    .append(bookFiles.getLoi().getCorrectedPath(bookFiles.getWorkingDirPath()))
                    .append("\"></reference>");
            sb.append(EOL);            
        }
        // text 	(First "real" page of content (e.g. "Chapter 1") )
        sb.append("    <!-- type=\"text\" [mandatory]: ")
                .append("a link to where the content of the book starts -->");
        sb.append(EOL);
        sb.append("    <reference type=\"text\" title=\"Start\" href=\"")
                .append(getStartOfBookReference())
                .append("\"></reference>");
        sb.append(EOL);
        sb.append("</guide>");
        sb.append(EOL);
        sb.append(EOL);
        return sb.toString();
    }      
    
    /**
     * 
     * @return 
     */
    private String createOpfFooter() {
        logger.fine("");
        StringBuilder sb = new StringBuilder();
        sb.append("</package>");
        return sb.toString();
    }
    
}   // OpfGenerator
